package com.gee.spring.cloud.data.transform.core.dispatch;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.gee.spring.cloud.common.core.api.result.ApiResult;
import com.gee.spring.cloud.common.core.domain.center.config.ChannelHandlerConfigDto;
import com.gee.spring.cloud.common.core.domain.center.config.SocketClientConfigDto;
import com.gee.spring.cloud.common.core.domain.center.config.SocketConfigDto;
import com.gee.spring.cloud.common.core.domain.socket.SocketType;
import com.gee.spring.cloud.common.core.entity.center.config.*;
import com.gee.spring.cloud.common.core.enums.SendReceive;
import com.gee.spring.cloud.common.core.enums.SocketOwnerType;
import com.gee.spring.cloud.common.core.util.json.JsonUtil;
import com.gee.spring.cloud.common.starter.feign.center.config.ChannelHandlerConfigClient;
import com.gee.spring.cloud.common.starter.feign.center.config.SocketClientConfigClient;
import com.gee.spring.cloud.common.starter.feign.center.config.SocketConfigClient;
import com.gee.spring.cloud.common.starter.socket.SocketHolder;
import com.gee.spring.cloud.common.starter.socket.coder.CoderSupplier;
import com.gee.spring.cloud.common.starter.socket.coder.DecoderFactory;
import com.gee.spring.cloud.data.transform.core.center.config.handler.*;
import com.gee.spring.cloud.data.transform.core.domain.mapping.ChannelHandlerConfigMapping;
import com.gee.spring.cloud.data.transform.core.domain.mapping.SocketClientConfigMapping;
import com.gee.spring.cloud.data.transform.core.domain.mapping.SocketConfigMapping;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

import static cn.hutool.core.text.StrPool.COMMA;

/**
 * @author gepengjun
 * @since 2024/6/19 14:45
 */
@Slf4j
@Component
public class SocketManager {

    private OrgCenter orgCenter;

    private SocketDecoder socketDecoder;

    private CoderSupplier coderSupplier;

    private final Map<Long ,SocketConfigDto> socketConfigDtoMap = new HashMap<>();

    private final Set<Frame> sendFrameSet = new HashSet<>();

    private final Set<Frame> receiveFrameSet = new HashSet<>();

    @Autowired
    private SocketConfigClient socketConfigClient;

    @Autowired
    private SocketDecoderHandler socketDecoderHandler;

    @Autowired
    private SocketClientConfigClient socketClientConfigClient;

    @Autowired
    private ChannelHandlerConfigClient channelHandlerConfigClient;

    @Autowired
    private SatelliteHandler satelliteHandler;

    @Autowired
    private DeviceHandler deviceHandler;

    @Autowired
    private FrameHandler frameHandler;

    @Autowired
    private TransformDslHandler transformDslHandler;

    public void init(OrgCenter orgCenter){
        this.orgCenter = orgCenter;
        socketDecoder = socketDecoderHandler.getById(orgCenter.getSocketDecoderId());
        coderSupplier = DecoderFactory.getCoderSupplier(socketDecoder);
        ApiResult<List<SocketConfig>> socketConfigApiResult = socketConfigClient.listByOwnerIdAndOwnerType(SocketOwnerType.ORG_CENTER.name(), orgCenter.getId());

        if (!socketConfigApiResult.isSuccess()){
            log.error("获取中心连接配置出错, 中心: {}, 接口错误信息: {}", orgCenter, socketConfigApiResult.getMsg());
            return;
        }
        List<SocketConfig> socketConfigList = socketConfigApiResult.getData();
        if (CollectionUtil.isEmpty(socketConfigList)){
            log.error("获取中心连接配置为空, 中心: {}", orgCenter);
            return;
        }

        socketConfigList.forEach(this::initSocket);
    }

    private void startAllSocket() {
        socketConfigDtoMap.values().forEach(this::startSocket);
    }

    private void startSocket(SocketConfigDto socketConfigDto){
        SocketHolder.initSocket(socketConfigDto, coderSupplier);
    }

    private void initSocket(SocketConfig socketConfig) {
        SocketConfigDto oldSocketConfigDto = socketConfigDtoMap.get(socketConfig.getId());
        SocketConfigDto socketConfigDto = SocketConfigMapping.Instance.toDto(socketConfig);
        if (oldSocketConfigDto != null){
            socketConfigDto.setClientConfigDtoMap(oldSocketConfigDto.getClientConfigDtoMap());
            socketConfigDto.setChannelHandlerConfMap(oldSocketConfigDto.getChannelHandlerConfMap());
        }
        socketConfigDtoMap.put(socketConfigDto.getId(), socketConfigDto);
        if (socketConfigDto.getSocketType().equals(SocketType.TCP_SERVER.name())){
            initSocketAllClient(socketConfigDto);
        }else {
            initSocketChannelHandler(socketConfigDto);
        }
        startSocket(socketConfigDto);
    }

    private void initSocketAllClient(SocketConfigDto socketConfigDto) {
        ApiResult<List<SocketClientConfig>> socketClientConfigListAr = socketClientConfigClient.listBySocketId(socketConfigDto.getId());
        if (socketClientConfigListAr.isSuccess() && CollectionUtil.isNotEmpty(socketClientConfigListAr.getData())){
            socketClientConfigListAr.getData().forEach(this::initSocketClient);
        }else {
            log.warn("未获取到客户端配置, socketConfig: \r\n{}", JsonUtil.toJsonStrPretty(socketConfigDto));
        }
    }

    private void initSocketClient(SocketClientConfig socketClientConfig){
        SocketClientConfigDto socketClientConfigDto = SocketClientConfigMapping.Instance.toDto(socketClientConfig);
        SocketConfigDto socketConfigDto = socketConfigDtoMap.get(socketClientConfig.getSocketId());
        if (socketConfigDto == null){
            log.warn("未匹配到socketConfigDto, socketClientConfig: \r\n{}", JsonUtil.toJsonStrPretty(socketClientConfig));
            return;
        }
        Map<Long, SocketClientConfigDto> clientConfigDtoMap = socketConfigDto.getClientConfigDtoMap();
        SocketClientConfigDto oldSocketClientConfigDto = clientConfigDtoMap.get(socketClientConfigDto.getId());
        if (oldSocketClientConfigDto != null){
            socketClientConfigDto.setChannelHandlerConfMap(oldSocketClientConfigDto.getChannelHandlerConfMap());
        }
        clientConfigDtoMap.put(socketClientConfigDto.getId(), socketClientConfigDto);
        initClientChannelHandler(socketClientConfigDto);
    }

    private void initClientChannelHandler(SocketClientConfigDto socketClientConfigDto) {
        ApiResult<List<ChannelHandlerConfig>> listApiResult = channelHandlerConfigClient.listByClientId(socketClientConfigDto.getId());
        if (listApiResult.isSuccess() && CollectionUtil.isNotEmpty(listApiResult.getData())){
            listApiResult.getData().forEach(this::initChannelHandler);
        }else {
            log.warn("未获取到信道处理器配置, SocketClientConfigDto: {}", socketClientConfigDto);
        }
    }

    private void initSocketChannelHandler(SocketConfigDto socketConfigDto) {
        ApiResult<List<ChannelHandlerConfig>> listApiResult = channelHandlerConfigClient.listBySocketId(socketConfigDto.getId());
        if (listApiResult.isSuccess() && CollectionUtil.isNotEmpty(listApiResult.getData())){
            listApiResult.getData().forEach(this::initChannelHandler);
        }else {
            log.warn("未获取到信道处理器配置, socketConfig: {}", socketConfigDto);
        }
    }

    private void initChannelHandler(ChannelHandlerConfig channelHandlerConfig){
        SocketConfigDto socketConfigDto = socketConfigDtoMap.get(channelHandlerConfig.getSocketId());
        if (socketConfigDto == null){
            log.error("未匹配到对应的socket配置, channelHandlerConfig: {}", channelHandlerConfig);
            return;
        }
        if (SocketType.TCP_SERVER.name().equals(socketConfigDto.getSocketType())){
            SocketClientConfigDto socketClientConfigDto = socketConfigDto.getClientConfigDtoMap().get(channelHandlerConfig.getClientId());
            if (socketClientConfigDto == null){
                log.error("未匹配到对应的client配置, channelHandlerConfig: {}", channelHandlerConfig);
                return;
            }
            ChannelHandlerConfigDto channelHandlerConfigDto = transformChannelHandlerConfig(channelHandlerConfig);
            socketClientConfigDto.getChannelHandlerConfMap().put(channelHandlerConfigDto.getId(), channelHandlerConfigDto);
        }else {
            ChannelHandlerConfigDto channelHandlerConfigDto = transformChannelHandlerConfig(channelHandlerConfig);
            socketConfigDto.getChannelHandlerConfMap().put(channelHandlerConfigDto.getId(), channelHandlerConfigDto);
        }
    }

    private ChannelHandlerConfigDto transformChannelHandlerConfig(ChannelHandlerConfig channelHandlerConfig){
        ChannelHandlerConfigDto channelHandlerConfigDto = ChannelHandlerConfigMapping.Instance.toDto(channelHandlerConfig);

        List<Device> filterDevList = transFilterDevIds(channelHandlerConfigDto.getFilterDeviceIds());
        channelHandlerConfigDto.setFilterDeviceList(filterDevList);

        List<Satellite> filterSateList = transFilterSateIds(channelHandlerConfigDto.getFilterSateIds());
        channelHandlerConfigDto.setFilterSateList(filterSateList);

        List<Frame> frameList = transFilterFrameIds(channelHandlerConfigDto.getFilterFrameIds());
        channelHandlerConfigDto.setFilterFrameList(frameList);

        if (channelHandlerConfigDto.getSendReceive().equals(SendReceive.SEND.name())){
            sendFrameSet.addAll(frameList);
        }else {
            receiveFrameSet.addAll(frameList);
        }

        TransformDsl transformDsl = transformDslHandler.getById(channelHandlerConfigDto.getTransformDslId());
        if (transformDsl == null){
            log.warn("未获取到信道处理脚本, 脚本id: {}", channelHandlerConfigDto.getTransformDslId());
        }else {
            channelHandlerConfigDto.setTransformDsl(transformDsl);
        }

        return channelHandlerConfigDto;
    }

    public List<Device> transFilterDevIds(String devIds){
        if (StrUtil.isNotEmpty(devIds)){
            String[] split = devIds.split(COMMA);
            return Arrays.stream(split)
                    .map(Long::parseLong)
                    .map(sateId -> deviceHandler.getById(sateId))
                    .collect(Collectors.toList());
        }
        return List.of();
    }

    public List<Satellite> transFilterSateIds(String sateIds){
        if (StrUtil.isNotEmpty(sateIds)){
            String[] split = sateIds.split(COMMA);
            return Arrays.stream(split)
                    .map(Long::parseLong)
                    .map(sateId -> satelliteHandler.getById(sateId))
                    .collect(Collectors.toList());
        }
        return List.of();
    }

    public List<Frame> transFilterFrameIds(String frameIds){
        if (StrUtil.isNotEmpty(frameIds)){
            String[] split = frameIds.split(COMMA);
            return Arrays.stream(split)
                    .map(Long::parseLong)
                    .map(sateId -> frameHandler.getById(sateId))
                    .collect(Collectors.toList());
        }
        return List.of();
    }

    public void updateSocketDecoder(SocketDecoder socketDecoder) {
        if (socketDecoder.getId().equals(this.socketDecoder.getId())){
            SocketHolder.stopAll();
            init(this.orgCenter);
        }
    }

    public void addSocketConfig(SocketConfig socketConfig) {
        if (SocketOwnerType.ORG_CENTER.name().equals(socketConfig.getOwnerType())
                && socketConfig.getOwnerId().equals(orgCenter.getId())){
            initSocket(socketConfig);
            startSocket(socketConfigDtoMap.get(socketConfig.getId()));
        }
    }


    public void updateSocketConfig(SocketConfig socketConfig) {
        SocketConfigDto socketConfigDto = socketConfigDtoMap.get(socketConfig.getId());
        if (socketConfigDto != null){
            SocketHolder.stopBySocketId(socketConfigDto.getId());
            initSocket(socketConfig);
            startSocket(socketConfigDtoMap.get(socketConfig.getId()));
        }
    }

    public void removeSocketConfig(SocketConfig socketConfig) {
        SocketConfigDto socketConfigDto = socketConfigDtoMap.get(socketConfig.getId());
        if (socketConfigDto != null){
            socketConfigDtoMap.remove(socketConfigDto.getId());
            SocketHolder.stopBySocketId(socketConfigDto.getId());
        }
    }


    public void addSocketClientConfig(SocketClientConfig socketClientConfig) {
        SocketConfigDto socketConfigDto = socketConfigDtoMap.get(socketClientConfig.getSocketId());
        if (socketConfigDto != null && SocketType.TCP_SERVER.name().equals(socketConfigDto.getSocketType())){
            initSocketClient(socketClientConfig);
        }
    }

    public void updateSocketClientConfig(SocketClientConfig socketClientConfig) {
        SocketConfigDto socketConfigDto = socketConfigDtoMap.get(socketClientConfig.getSocketId());
        if (socketConfigDto != null && SocketType.TCP_SERVER.name().equals(socketConfigDto.getSocketType())){
            initSocketClient(socketClientConfig);
        }
    }

    public void removeSocketClientConfig(SocketClientConfig socketClientConfig) {
        SocketConfigDto socketConfigDto = socketConfigDtoMap.get(socketClientConfig.getSocketId());
        if (socketConfigDto != null && socketConfigDto.getSocketType().equals(SocketType.TCP_SERVER.name())){
            SocketClientConfigDto removed = socketConfigDto.getClientConfigDtoMap().remove(socketClientConfig.getId());
            if (removed != null){
                log.info("删除TCP_SERVER的客户端配置成功: {}", removed);
            }else {
                log.warn("删除TCP_SERVER的客户端配置失败,socketClientConfig {}", socketClientConfig);
            }
        }
    }

    public void addChannelHandlerConfig(ChannelHandlerConfig channelHandlerConfig) {
        initChannelHandler(channelHandlerConfig);
    }

    public void updateChannelHandlerConfig(ChannelHandlerConfig channelHandlerConfig) {
        initChannelHandler(channelHandlerConfig);
    }

    public void removeChannelHandlerConfig(ChannelHandlerConfig channelHandlerConfig) {
        SocketConfigDto socketConfigDto = socketConfigDtoMap.get(channelHandlerConfig.getSocketId());
        if (socketConfigDto == null){
            return;
        }
        if (SocketType.TCP_SERVER.name().equals(socketConfigDto.getSocketType())){
            SocketClientConfigDto socketClientConfigDto = socketConfigDto.getClientConfigDtoMap().get(channelHandlerConfig.getClientId());
            if (socketClientConfigDto == null){
                return;
            }
            ChannelHandlerConfigDto removed = (ChannelHandlerConfigDto)socketClientConfigDto.getChannelHandlerConfMap().remove(channelHandlerConfig.getId());
            if (removed != null){
                log.info("去除信道处理器: \r\n{}", JsonUtil.toJsonStrPretty(removed));
            }
        }
    }

    public void updateDevice(Device device) {
        socketConfigDtoMap.values().forEach(socketConfigDto -> {
            if (SocketType.TCP_SERVER.name().equals(socketConfigDto.getSocketType())){
                socketConfigDto.getClientConfigDtoMap().values().forEach(client -> {
                    client.getChannelHandlerConfMap().values().forEach(iChannelHandlerConf -> {
                        ChannelHandlerConfigDto channelHandlerConfigDto = (ChannelHandlerConfigDto)iChannelHandlerConf;
                        List<Device> deviceList = channelHandlerConfigDto.getFilterDeviceList();
                        updateFilterDevice(deviceList, device);
                    });
                });
            }else {
                socketConfigDto.getChannelHandlerConfMap().values().forEach(iChannelHandlerConf -> {
                    ChannelHandlerConfigDto channelHandlerConfigDto = (ChannelHandlerConfigDto)iChannelHandlerConf;
                    List<Device> deviceList = channelHandlerConfigDto.getFilterDeviceList();
                    updateFilterDevice(deviceList, device);
                });
            }
        });
    }

    public static void updateFilterDevice(List<Device> deviceList, Device device){
        if (CollectionUtil.isNotEmpty(deviceList)){
            Optional<Device> first = deviceList.stream()
                    .filter(d -> d.getId().equals(device.getId()))
                    .findFirst();
            if (first.isPresent()){
                Device oldDevice = first.get();
                int index = deviceList.indexOf(oldDevice);
                deviceList.add(index, device);
            }
        }
    }

    public void removeDevice(Device device) {
        socketConfigDtoMap.values().forEach(socketConfigDto -> {
            if (SocketType.TCP_SERVER.name().equals(socketConfigDto.getSocketType())){
                socketConfigDto.getClientConfigDtoMap().values().forEach(client -> {
                    client.getChannelHandlerConfMap().values().forEach(iChannelHandlerConf -> {
                        ChannelHandlerConfigDto channelHandlerConfigDto = (ChannelHandlerConfigDto)iChannelHandlerConf;
                        List<Device> deviceList = channelHandlerConfigDto.getFilterDeviceList();
                        removeFilterDevice(deviceList, device);
                    });
                });
            }else {
                socketConfigDto.getChannelHandlerConfMap().values().forEach(iChannelHandlerConf -> {
                    ChannelHandlerConfigDto channelHandlerConfigDto = (ChannelHandlerConfigDto)iChannelHandlerConf;
                    List<Device> deviceList = channelHandlerConfigDto.getFilterDeviceList();
                    removeFilterDevice(deviceList, device);
                });
            }
        });
    }

    public static void removeFilterDevice(List<Device> deviceList, Device device){
        if (CollectionUtil.isNotEmpty(deviceList)){
            Optional<Device> first = deviceList.stream()
                    .filter(d -> d.getId().equals(device.getId()))
                    .findFirst();
            if (first.isPresent()){
                Device oldDevice = first.get();
                deviceList.remove(oldDevice);
            }
        }
    }

    public void updateTransformDsl(TransformDsl newTransformDsl, TransformDsl oldTransformDsl){
        socketConfigDtoMap.values().forEach(socketConfigDto -> {
            if (socketConfigDto.getSocketType().equals(SocketType.TCP_SERVER.name())){
                socketConfigDto.getClientConfigDtoMap().values().forEach(clientConfigDto -> {
                    clientConfigDto.getChannelHandlerConfMap().values().stream()
                            .map(iChannelHandlerConf -> (ChannelHandlerConfigDto)iChannelHandlerConf)
                            .filter(channelHandlerConfigDto -> channelHandlerConfigDto.getTransformDslId().equals(oldTransformDsl.getId()))
                            .forEach(channelHandlerConfigDto -> channelHandlerConfigDto.setTransformDsl(newTransformDsl));
                });
            }else {
                socketConfigDto.getChannelHandlerConfMap().values().stream()
                        .map(iChannelHandlerConf -> (ChannelHandlerConfigDto)iChannelHandlerConf)
                        .filter(channelHandlerConfigDto -> channelHandlerConfigDto.getTransformDslId().equals(oldTransformDsl.getId()))
                        .forEach(channelHandlerConfigDto -> channelHandlerConfigDto.setTransformDsl(newTransformDsl));
            }
        });
    }

    public Map<Long, SocketConfigDto> getSocketConfigDtoMap() {
        return socketConfigDtoMap;
    }

    public Set<Frame> getSendFrameSet() {
        return sendFrameSet;
    }

    public Set<Frame> getReceiveFrameSet() {
        return receiveFrameSet;
    }
}
